#include "tbcore/base/file.hpp"

#include "tbcore/base/string/string_util.hpp"

#include "tbcore/base/tostring.hpp"

TB_NAMESPACE_BEGIN

static const TCHAR* kHtmlDocumentExts[] = {_T(".html"), _T(".html"), _T(".xhtml")};

static const TCHAR* kSourceCodeExts[] = {
  _T(".i"), _T(".h"), _T(".hpp"), _T(".hxx"), _T(".hm"), _T(".inl"), 
  _T(".inc"), _T(".xsd"), _T(".cpp"), _T(".c"), _T(".cc"), _T(".cxx"), 
  _T(".hpj"), _T(".asm"), _T(".asmx")};

static const TCHAR* kScriptFileExts[] = {_T(".py"), _T(".rb"), _T(".vb"), _T(".js"), _T(".lua")};

static bool EndsWithExts(const String& filename, const TCHAR** exts, uint32 size) {
  for (uint32 i = 0; i < size; ++i) {
    if (EndsWith(filename, exts[i], false)) {
      return true;
    }
  }
  return false;
}

bool File::IsBinaryFile( const String& filename ) {
  if (EndsWith(filename, _T(".bin"), false)) {
    return true;
  }
  return false;
}

bool File::IsExecutionFile( const String& filename ) {
  if (EndsWith(filename, _T("exe"), false)) {
    return true;
  }
  return false;
}

bool File::IsDynamicLibrary( const String& filename ) {
  if (EndsWith(filename, _T(".dll"), false) || EndsWith(filename, _T(".dll"), false)) {
    return true;
  }
  return false;
}

bool File::IsStaticLibrary( const String& filename ) {
  if (EndsWith(filename, _T(".a"), false) || 
    EndsWith(filename, _T(".lib"), false) ||
    EndsWith(filename, _T(".dylib"), false)) {
    return true;
  }
  return false;
}

bool File::IsTextDocument( const String& filename ) {
  if (EndsWith(filename, _T(".txt"), false)) {
    return true;
  }
  return false;
}

bool File::IsMarkdownDocument( const String& filename ) {
  if (EndsWith(filename, _T(".md"), false) || 
    EndsWith(filename, _T(".markdown"), false)) {
    return true;
  }
  return false;
}

bool File::IsXmlDocument( const String& filename ) {
  if (EndsWith(filename, _T(".xml"), false)) {
    return true;
  }
  return false;
}

bool File::IsHtmlDocument( const String& filename ) {
  if (EndsWith(filename, _T(".html"), false) || 
    EndsWith(filename, _T(".html"), false) ||
    EndsWith(filename, _T(".xhtml"), false)) {
    return true;
  }
  return false;
}

bool File::IsJsonDocument( const String& filename ) {
  if (EndsWith(filename, _T(".json"), false)) {
    return true;
  }
  return false;
}

bool File::IsWordDocument( const String& filename ) {
  if (EndsWith(filename, _T(".doc"), false) || 
    EndsWith(filename, _T(".docx"), false)) {
      return true;
  }
  return false;
}

bool File::IsPPTDocument( const String& filename ) {
  if (EndsWith(filename, _T(".ppt"), false) || 
    EndsWith(filename, _T(".pptx"), false)) {
     return true;
  }
  return false;
}

bool File::IsXpsDocument( const String& filename ) {
  if (EndsWith(filename, _T(".xps"), false)) {
    return true;
  }
  return false;
}

bool File::IsVisioDocument( const String& filename ) {
  if (EndsWith(filename, _T(".visio"), false)) {
    return true;
  }
  return false;
}

bool File::IsIniDocument( const String& filename ) {
  if (EndsWith(filename, _T(".ini"), false)) {
    return true;
  }
  return false;
}

bool File::IsSourceCode( const String& filename ) {
  return EndsWithExts(filename, kSourceCodeExts, TB_ARRAYSIZE(kSourceCodeExts));
}

bool File::IsBatchFile( const String& filename ) {
  if (EndsWith(filename, _T(".bat"), false) || 
    EndsWith(filename, _T(".cmd"), false)) {
     return true;
  }
  return false;
}

bool File::IsScriptLua(const String& filename) {
  return EndsWith(filename, _T(".lua"), false);
}

bool File::IsScriptFile( const String& filename ) {
  return EndsWithExts(filename, kSourceCodeExts, TB_ARRAYSIZE(kSourceCodeExts));
}

File::FileType File::GetFileType( const String& filename ) {
  if (IsBinaryFile(filename)) {
    return kBinaryFile;
  } else if (IsExecutionFile(filename)) {
    return kExecutionFile;
  } else if (IsDynamicLibrary(filename)) {
    return kDynamicLibrary;
  } else if (IsStaticLibrary(filename)) {
    return kStaticLibrary;
  } else if (IsTextDocument(filename)) {
    return kTextDocument;
  } else if (IsMarkdownDocument(filename)) {
    return kMarkdownDocument;
  } else if (IsXmlDocument(filename)) {
    return kXmlDocument;
  } else if (IsHtmlDocument(filename)) {
    return kHtmlDocument;
  } else if (IsJsonDocument(filename)) {
    return kJsonDocument;
  } else if (IsWordDocument(filename)) {
    return kWordDocument;
  } else if (IsPPTDocument(filename)) {
    return kPPTDocument;
  } else if (IsVisioDocument(filename)) {
    return kVisioDocument;
  } else if (IsIniDocument(filename)) {
    return kIniDocument;
  } else if (IsSourceCode(filename)) {
    return kSourceCode;
  } else if (IsBatchFile(filename)) {
    return kBatchFile;
  } else if (IsScriptLua(filename)) {
    return kScriptLua;
  } else if (IsScriptFile(filename)) {
    return kScriptFile;
  }
  return kUnknownFileType;
}

String File::GetFileExtension( const String& filename ) {
  for (String::const_iterator it = filename.end(); it != filename.begin(); --it) {
    if (*it == _T('.')) {
      return &(*it);
    }
  }
  return String();
}

String File::GetFileFullExtension( const String& filename ) {
  for (String::const_iterator it = filename.begin(); it != filename.end(); ++it) {
    if ((*it) == _T('.')) {
      return &(*it);
    }
  }
  return String();
}

Result File::WriteBinary(const String &path, MemoryBuffer& data) {
	FILE* fp;
	if (fopen_ys(&fp, path.c_str(), _T("wb")) == 0) {
		tuple<const char*, uint32> tmp;
		std::size_t bytesWritten;
		while (true) {
			tmp = data.ReadBytes();
			if (get<1>(tmp) > 0) {
				bytesWritten = fwrite(get<0>(tmp), sizeof(char), get<1>(tmp), fp);
				if (bytesWritten != get<1>(tmp)) {
					fclose(fp);
					return Result(kWriteFileFailed, _T("not all data written!"));
				}
			} else {
				break;
			}
		}
		fclose(fp);
	} else {
		return Result(kOpenFileFlaied, _T("open file by wb mode failed!"));
	}
	return Result();
}

Result File::ReadBinary(const String &path, MemoryBuffer& data) {
  FILE *fp;
  const std::size_t kReadBufferSize = 4096;
  std::size_t sizeRead;
  if (fopen_ys(&fp, path.c_str(), _T("rb")) == 0) {
    char buf[kReadBufferSize];
    sizeRead = fread(buf, sizeof(char), kReadBufferSize, fp);
    if (sizeRead > 0) {
      data.WriteBytes(buf, (uint32)sizeRead);
    }
  } else {
    return Result(kOpenFileFlaied, _T("open file by rb mode failed!"));
  }
  return Result();
}

TB_NAMESPACE_END